package download_temp;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import cn.lby.tp.downloadtest.BaseApplication;
import download_temp.view.OnProgressListener;

public class MultiDownloader {

    // 线程的数量
    public int threadCount = 4;

    // 正在运行的线程的个数
    private int runningThreadCount;

    // 文件下载地址
    private String downUrl;

    // 下载文件名（不带路径）
    private String targetFile;
    // 线程组
    private DownloadThread[] threads;

    // 定义下载的文件的总大小
    private int fileSize;
    private int buffSize = 1024 * 4;


    public void setBuffSize(int buffSize) {
        this.buffSize = buffSize;
    }

    public void setThreadCount(int threadCount) {
        this.threadCount = threadCount;
    }

    public MultiDownloader(String targetFile, String downUrl) {
        this.downUrl = downUrl;
        this.targetFile = targetFile;
    }


    // 获取下载的完成百分比
    public double getCompleteRate() {
        // 统计多条线程已经下载的总大小
        int sumSize = 0;
        for (int i = 0; i < threadCount; i++) {
            sumSize += threads[i].length;
        }
        // 返回已经完成的百分比


        double v = sumSize * 100.0 / fileSize;
        // 如果监听器不为空，则回调下载进度
        Message msg = new Message();
        msg.what = 0x123;
        msg.arg1 = 100;
        msg.arg2 = (int) v;
        handler.sendMessage(msg);
        return v;
    }


    private void getLength()  {
        try {
            URL url = new URL(downUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            int code = conn.getResponseCode();
            if (code == 200) {
                // 获取服务器文件的大小
                fileSize = conn.getContentLength();





                // 断开http
                conn.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void download() throws IOException {
        getLength();

        if (fileSize < 0) {
            Looper.prepare();
            Toast.makeText(BaseApplication.context, "下载地址访问失败", Toast.LENGTH_SHORT).show();
            return;
        }

        //1.在本地创建一个大小和服务器一模一样的空文件。RandomAccessFile
        File file = new File(BaseApplication.DOWNLOAD_PATH, targetFile);
        RandomAccessFile raf = new RandomAccessFile(file, "rwd");
        raf.setLength(fileSize);
        close(raf);

        this.threads = new DownloadThread[threadCount];
        //2.等份服务器的资源为若干份，让每个子线程下载自己的部分。
        int blockers = fileSize / threadCount;
        runningThreadCount = threadCount;
        for (int tid = 0; tid < threadCount; tid++) {
            int startIndex = tid * blockers;
            int endIndex = (tid + 1) * (blockers) - 1;
            //特殊情况
            if (tid == (threadCount - 1)) {
                //最后一个线程。结束位置为文件总长度-1
                endIndex = fileSize - 1;
            }
            threads[tid] = new DownloadThread(tid, startIndex, endIndex, downUrl);
            threads[tid].start();
        }
    }


public class DownloadThread extends Thread {
    // 当前线程 id
    private final int threadId;
    private int startIndex;
    //
    private final int endIndex;
    // 资源下载地址
    private final String downUrl;
    // 定义该线程已下载的字节数
    private int length;

    public DownloadThread(int threadId, int startIndex, int endIndex, String downUrl) {
        this.threadId = threadId;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        this.downUrl = downUrl;
    }


    @Override
    public void run() {


        // System.out.println("线程id:" + threadId + "理论下载位置：" + startIndex + "~" + endIndex);
        try {
            URL url = new URL(downUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            // 查看文件里面是否记录有当前线程的下载开始位置。如果有记录则从记录处开始，否则从 startIndex 处开始
            ssRecorded(conn);

            //200 OK  206 请求部分数据成功
            int code = conn.getResponseCode();
            // System.out.println("返回状态码：code = " + code);

            if (code == 206) {
                //当前的线程 就应用下载这一部分的数据。
                InputStream is = conn.getInputStream();
                File f = new File(BaseApplication.DOWNLOAD_PATH, targetFile);
                RandomAccessFile raf = new RandomAccessFile(f, "rwd");
                // 注意：不同线程存文件的开始位置是不相同的，要从自己对应的位置存文件
                raf.seek(startIndex);

                // 缓冲区
                byte[] buffer = new byte[buffSize];
                int len = -1;
                while ((len = is.read(buffer)) != -1) {
                    raf.write(buffer, 0, len);
                    length += len;

                    // 当前线程已下载到的位置。
                    int currentPosition = startIndex + length;

                    // System.err.println("线程id：" + threadId + " 下载到:" + currentPosition);

                    // 获取下载进度百分比
                    double completeRate = getCompleteRate();
                    Log.e("下载进度", String.valueOf(completeRate));


                    // 用于保存当前线程已下载到的位置
                    File file = new File(BaseApplication.DOWNLOAD_PATH, threadCount + "-temp-" + threadId + ".txt");

                    //确保了每次循环都会把进度写到底层存储设备里面。
                    /**  r: 文件只读方式打开
                     *   rw: 文件读写方式打开
                     * rws: 文件读写方式打开,对文件内容或者元数据的每个更新都同步到写入底层设备
                     rwd:文件读写方式打开, 对文件内容的每个更新都写入到底层设备，断电了数据也立刻写入
                     **/
                    RandomAccessFile raFos = new RandomAccessFile(file, "rwd");
                    // 把当前线程的位置信息写入到一个文件里面
                    raFos.write(String.valueOf(currentPosition).getBytes());
                    // 数据并不是直接保存到底层的存储设备里面，保存到缓存，缓存空间满了，数据会同步到底层设备。
                    close(raFos);
                }

                close(is, raf);
                System.out.println("线程：" + threadId + "下载完毕了。");


                synchronized (MultiDownloader.class) {
                    runningThreadCount--;

                    if (runningThreadCount <= 0) {
                        System.out.println("线程全部下载完毕了。");
                        // 删除临时文件
                        for (int i = 0; i < threadCount; i++) {
                            File f2 = new File(BaseApplication.DOWNLOAD_PATH, threadCount + "-temp-" + i + ".txt");
                            f2.delete();
                        }
                    }

                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    // 查看文件里面是否记录有当前线程的下载开始位置。
    private void ssRecorded(HttpURLConnection conn) throws IOException {
        File file = new File(BaseApplication.DOWNLOAD_PATH, threadCount + "-temp-" + threadId + ".txt");
        if (file.exists() && file.length() > 0) {
            FileInputStream fis = new FileInputStream(file);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String line = br.readLine();
            conn.setRequestProperty("Range", "bytes=" + line + "-" + endIndex);

            // System.out.println("线程id："+ threadId +"真实的下载位置："+ line +"~"+ endIndex);

            // 更新开始位置为文件记录处
            startIndex = Integer.parseInt(line);
            // 记得释放文件的引用
            close(fis);
        } else {
            // 不是下载整个文件，而是下载文件的一部分。 告诉服务器 下载的资源就是一部分
            // HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");
            conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

            // System.out.println("线程id：" + threadId + "真实的下载位置：" + startIndex + "~" + endIndex);
        }
    }

}

    // 关闭 I/O 流快捷方法
    public void close(Closeable... closeableList) {
        for (Closeable closeable : closeableList) {
            try {
                if (closeable != null) closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    private OnProgressListener listener;//进度回调监听

    public void setListener(OnProgressListener listener) {
        this.listener = listener;
    }

    /**
     * 更新进度
     */
    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x123:
                    if (listener != null) {
                        listener.updateProgress(msg.arg1, msg.arg2);
                    }
                    break;
            }
        }
    };

}